﻿// <copyright file="CrossSection.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>CrossSection class</summary>

namespace BESASM.EMGAATS.BusinessClasses
{
  using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;

  /// <summary>
  /// Manages link cross sections
  /// </summary>
  public class CrossSection : Entity, ICrossSection
  {
    private string name;
    private Dictionary<double, double> ratios = new Dictionary<double, double>();

    /// <summary>
    /// Initializes a new instance of the <see cref="CrossSection" /> class.
    /// </summary>
    /// <param name="id">ID of the new cross section</param>
    public CrossSection(int id)
      : base(EntityType.CrossSection, id)
    {
      this.name = string.Format("CrossSection_{0}", id);
    }

    /// <summary>
    /// Gets or sets a user friendly name for this cross section shape.
    /// </summary>
    public string CrossSectionName 
    {
      get
      {
        return this.name;
      }

      set
      {
        if (string.IsNullOrWhiteSpace(value))
        {
          throw new ArgumentException("The cross section name cannot be null or empty.");
        }

        if (value != this.name)
        {
          this.name = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets the set of ratios that define this link cross section.
    /// </summary>
    public IDictionary<double, double> Ratios
    {
      get 
      {
        return new ReadOnlyDictionary<double, double>(this.ratios);
      }
    }

    /// <summary>
    /// The calculated area in square inches of the cross section given a specific height in inches.
    /// </summary>
    /// <param name="heightIn">The height of the cross section in inches.</param>
    /// <returns>The area of the cross section, in square inches</returns>
    public double GetAreaSqIn(double heightIn)
    {
      if (heightIn <= 0.0)
      {
        throw new ArgumentException("The height must be a positive number.");
      }

      if (this.ratios.Count < 2.0)
      {
        throw new InvalidOperationException("The cross section must have at least 2 ratios defined to calculate area.");
      }

      if (!this.ratios.ContainsKey(0.0) || !this.ratios.ContainsKey(1.0))
      {
        throw new InvalidOperationException("The cross section does not have widths defined for its full height.");
      }

      // Calculate relative area
      double totalArea = 0.0;

      List<double> heights = new List<double>(this.ratios.Keys);
      heights.Sort();
      for (int i = 0; i < heights.Count - 1; i++)
      {
        double w1 = this.ratios[heights[i]];
        double w2 = this.ratios[heights[i + 1]];
        double area = ((w1 + w2) / 2.0) * (heights[i + 1] - heights[i]);
        totalArea += area;
      }

      // Return absolute area
      return totalArea * heightIn * heightIn;
    }

    /// <summary>
    /// Adds a ratio to the cross section
    /// </summary>
    /// <param name="relativeHeight">Relative height compared to full height</param>
    /// <param name="relativeWidth">Relative width compared to full width</param>
    public void AddRatio(double relativeHeight, double relativeWidth)
    {
      if (relativeHeight < 0.0 || relativeHeight > 1.0)
      {
        throw new ArgumentException("The height must be a relative value from 0.0 to 1.0", "height");
      }

      if (relativeWidth < 0.0)
      {
        throw new ArgumentException("The relative width cannot be negative.", "width");
      }

      if (!this.ratios.ContainsKey(relativeHeight) || this.ratios[relativeHeight] != relativeWidth)
      {
        this.ratios[relativeHeight] = relativeWidth;
        this.NotifyPropertyChanged("Ratios");
      }
    }

    /// <summary>
    /// Creates a new entity that is a copy of the current instance.
    /// </summary>
    /// <returns>A new object that is a shallow copy of this instance.</returns>
    public override object Clone()
    {
      CrossSection clone = new CrossSection(this.Id);

      // Simple properties and references
      // DO NOT copy Network, clones must be added to a network
      clone.name = this.name;

      // Ratios - do not copy collection (i.e. the dictionary), but copy values
      foreach (var ratio in this.ratios)
      {
        clone.ratios.Add(ratio.Key, ratio.Value);
      }

      this.CloneAuditProperties(clone);

      clone.SetModified(false);
      return clone;
    }

    /// <summary>
    /// Returns a string representation of this cross section.
    /// </summary>
    /// <returns>A string representation of this cross section.</returns>
    public override string ToString()
    {
      return string.Format("{1} (Id: {0})", this.Id, this.CrossSectionName);
    }
  }
}
